#include "Astronomy/JDateTime.hpp"
#include <cmath>

unsigned short JDateTime::getWeekday(int year, int month, int day)
{
    int c     = year / 100;
    int jYear = year % 100;

    // 0年 改成 -1年;
    if (year == 0)
        year = -1;

    // 计算当前天数;
    const int currerntDays = (day + 31 * (month + year * 12));

    // 用于修正1582-10-4 ~ 1582-10-15 日历 修改修正问题;
    int variable           = 4 - c;

    if (currerntDays > JULIAN_LAST_DAY) //  当前日期大于 1582-10-4;
    {
        if (currerntDays <= GREGORIAN_FIRST_DAY) // 1582-10-5 ~ 1582-10-15  期间的日期由于被抹掉了，这里我们将这几天的日期均代表 15号;
        {
            day = 15;
        }
        variable = c / 4 - 2 * c - 1;
    }
    // int w = d + 13 * (m + 1) / 5 + y + y / 4 + 4 - c;             //For Gregorian
    // int w = d + 13 * (m + 1) / 5 + y + y / 4 + c / 4 - 2 * c - 1; //For Julian
    int w = day + 13 * (month + 1) / 5 + jYear + jYear / 4 + variable;
    while (w < 0)
    {
        w += 7;
    }
    return (unsigned short)(w % 7);
}

JDateTime JDateTime::of(double jd, int tz)
{
    int    z   = (int)std::floor(jd + 0.5);
    double f   = jd + 0.5 - z;
    int    adj = z;
    if (jd >= JULIAN_GREGORIAN_BOUNDARY)
    {
        int alpha = (int)((z - 1867216.25) / 36524.25);
        adj       = z + 1 + alpha - alpha / 4;
    }
    double b    = adj + 1524;
    double c    = std::floor((b - 122.1) / 365.25);
    double d    = std::floor(365.25 * c);
    double e    = std::floor((b - d) / 30.6001);
    double dd   = b - d - std::floor(30.6001 * e) + f;
    int    mm   = e <= 13 ? e - 1 : e - 13;
    int    yyyy = mm <= 2 ? c - 4715 : c - 4716;
    // 0年 = -1年,所有全部需要-1;
    yyyy        = ((yyyy <= 0) ? yyyy - 1 : yyyy);

    int date    = (int)std::floor(dd);
    dd          = (dd - date) * 24;
    int hour    = (int)std::floor(dd);
    dd          = (dd - hour) * 60;
    int minute  = (int)std::floor(dd);
    dd          = (dd - minute) * 60;
    int second  = (int)std::floor(dd);
    dd          = (dd - second);

    if (dd >= 0.5)
        second++;
    return {yyyy, (unsigned short)mm, (unsigned short)date, (unsigned short)hour, (unsigned short)minute, (unsigned short)second};
}

double JDateTime::of(int year, unsigned short month, unsigned short day, unsigned short hour, unsigned short minute, unsigned short second)
{
    // 0年 = -1年,也可抛出异常;
    if (year == 0)
        year = -1;

    // BCE:公元前我们用负数表示,并向上加一年为了日期的连续型;
    if (year < 0)
    {
        year = year + 1;
    }
    const int adj                   = (14 - month) / 12;
    const int iy                    = year + 4800 - adj;
    const int im                    = month + 12 * adj - 3;

    // 计算当前天数;
    const int currerntDays          = (day + 31 * (month + year * 12));

    // 用于修正1582-10-4 ~ 1582-10-15 日历 修改修正问题;
    int NumberOfLeapYearCorrections = 0;

    if (currerntDays > JULIAN_LAST_DAY) //  当前日期大于 1582-10-4;
    {
        if (currerntDays <= GREGORIAN_FIRST_DAY) // 1582-10-5 ~ 1582-10-15  期间的日期由于被抹掉了，这里我们将这几天的日期均代表 15号;
        {
            day = 15;
        }
        NumberOfLeapYearCorrections = -(iy / 100) + iy / 400 + 38; // 38 = 32083 - 35045;
    }
    // return date + (153 * m + 2) / 5 + 365 * y + y / 4 - 32083;                      //For Julian
    // return date + (153 * m + 2) / 5 + 365 * y + y / 4 - y / 100 + y / 400 - 32045;  //For Gregorian

    int jdn = day + (153 * im + 2) / 5 + 365 * iy + iy / 4 + NumberOfLeapYearCorrections - 32083;

    return jdn + (hour - 12) / 24.0 + minute / 1440.0 + second / 86400.0;
}

const unsigned short JDateTime::getWeekDay() const
{
    return this->getWeekday(this->dateYear, this->dateMonth, this->dateDay);
};

const int JDateTime::getDaysOfMonth(int year, unsigned int month) noexcept
{
    const unsigned short daysOfMonth[2][12] = {{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}};
   
    return daysOfMonth[JDateTime::isLeapYear(year) ? 1 : 0][month - 1];
}

void JDateTime::setDate(int year, unsigned short month, unsigned short day)
{

    if (year == 0)
        year = -1;
    // 判断月份;
    if (month > 12)
        month = 12;
    if (month < 1)
        month = 1;

    // 判断日数;
    if (day < 1)
        day = 1;

    auto maxDays = getDaysOfMonth(year, month);

    if (maxDays < day)
        day = maxDays;

    this->dateYear      = year;
    this->dateMonth     = month;
    this->dateDay       = day;

    this->dateJulianDay = this->of(year, month, day, this->dateHour, this->dateMinute, this->dateSecond);
}

std::string JDateTime::ToTimeString(double jd) noexcept
{
    jd += 0.5;
    jd    = (jd - floor(jd));

    int s = (int)floor(jd * 86400.0 + 0.5);
    int h = (int)floor(s / 3600.0);
    s -= h * 3600.0;
    int m = (int)floor(s / 60.0);
    s -= m * 60;

    auto str = std::format("{:02d}:{:02d}:{:02d}", h, m, s);
    return str;
}